#pragma once
#include <Rz/Types.h>
#include <Rz/IEquatable.h>
#include <Rz/ITypeWrapper.h>
#include <Rz/Collections/List.h>
#include <Rz/Collections/IEnumerable.h>
#include <Rz/Collections/IList.h>
#include <Rz/Collections/InitializerList.h>

#ifdef RZ_COMPILER_MSVC
#	include <windows.h>

#	ifdef GetClassName
#		undef GetClassName
#	endif
#else
typedef char TCHAR;
#endif

#include <cstdlib>
#include <string>
#include <algorithm>
#include <cctype>
#include <cwchar>

namespace Rz {

template <typename T>
struct _BasicString_Identity
{
	typedef T type;
};

template <typename T>
class BasicString :
	public Object,
	public Collections::IList<T>,
	public IEquatable<BasicString<T>>
{
	public:
		RZ_RTTI(Rz, BasicString);
		RZ_RTTI_IMPLEMENTS(Object, Collections::IList<T>, IEquatable<BasicString<T>>);
		RZ_RTTI_TEMPLATES(NonObjectType<T>);
		RZ_RTTI_END;

		RZ_TYPEWRAPPER_IMPLEMENT(T);

		typedef Collections::List<BasicString<T>> Collection;

		static ConstPointer Whitespaces;

		enum class SplitOptions : u8
		{
			None,
			RemoveEmptyEntries
		};

		enum class Case : u8
		{
			NoticeCase,
			IgnoreCase
		};

		struct WideChar
		{
			static SizeType GetFinalStringLength(const wchar_t* utf8);
			static bool ToMultiByte(const wchar_t* utf8, char* ansi, SizeType ansiBuffLen);
		};

		struct MultiByte
		{
			static s32 GetFinalStringLength(const char* ansi);
			static bool ToWideChar(const char* ansi, wchar_t* utf8, SizeType utf8BuffLen);
		};

		// implementation

		BasicString();
		BasicString(ValueType s);
		BasicString(ConstPointer s);
		BasicString(const std::basic_string<T>& s);
		BasicString(const BasicString<T>& s);
		BasicString(std::basic_string<T>&& s);
		BasicString(BasicString<T>&& s);

		BasicString<T>& operator += (ValueType s);
		BasicString<T>& operator += (ConstPointer s);
		BasicString<T>& operator += (const BasicString<T>& s);

		BasicString<T>  operator =  (ValueType s);
		BasicString<T>  operator =  (ConstPointer s);
		BasicString<T>  operator =  (const BasicString<T>& s);

		RZ_INLINE friend BasicString<T> operator + (ValueType l, const BasicString<T>& r)
		{
			BasicString<T> tmp(l);
			tmp += r;
			return tmp;
		}
		
		RZ_INLINE friend BasicString<T> operator + (ConstPointer l, const BasicString<T>& r)
		{
			BasicString<T> tmp(l);
			tmp += r;
			return tmp;
		}

		RZ_INLINE friend BasicString<T> operator + (const BasicString<T>& l, const BasicString<T>& r)
		{
			BasicString<T> tmp(l);
			tmp += r;
			return tmp;
		}

		RZ_INLINE friend BasicString<T> operator + (const BasicString<T>& l, ConstPointer r)
		{
			BasicString<T> tmp(l);
			tmp += r;
			return tmp;
		}

		RZ_INLINE friend bool operator == (ValueType l, const BasicString<T>& r)
		{
			return r.Equals(l);
		}
		
		RZ_INLINE friend bool operator == (ConstPointer l, const BasicString<T>& r)
		{
			return r.Equals(l);
		}

		RZ_INLINE friend bool operator != (ValueType l, const BasicString<T>& r)
		{
			return !r.Equals(l);
		}

		RZ_INLINE friend bool operator != (ConstPointer l, const BasicString<T>& r)
		{
			return !r.Equals(l);
		}

		RZ_INLINE friend std::ostream& operator << (std::ostream& out, const BasicString<T>& s)
		{
			return (out << s._string);
		}

		RZ_INLINE friend std::wostream& operator << (std::wostream& out, const BasicString<T>& s)
		{
			return (out << s._string);
		}

		BasicString<T>& Append(ValueType s);
		BasicString<T>& Append(ConstPointer s);
		BasicString<T>& Append(const BasicString<T>& s);

		bool Equals(ValueType s) const;
		bool Equals(ConstPointer s) const;
		bool Equals(T s, Case caseType) const;
		bool Equals(ConstPointer s, Case caseType) const;
		bool Equals(const BasicString<T>& s, Case caseType) const;

		SizeType IndexOf(ValueType s) const;
		SizeType IndexOf(ConstPointer s) const;	
		SizeType IndexOf(const BasicString<T>& s) const;
		SizeType IndexOf(ValueType s, Case caseType) const;
		SizeType IndexOf(ConstPointer s, Case caseType) const;
		SizeType IndexOf(const BasicString<T>& s, Case caseType) const;
		SizeType IndexOf(ValueType s, SizeType offset) const;
		SizeType IndexOf(ConstPointer s, SizeType offset) const;
		SizeType IndexOf(const BasicString<T>& s, SizeType offset) const;
		SizeType IndexOf(ValueType s, Case caseType, SizeType offset) const;
		SizeType IndexOf(ConstPointer s, Case caseType, SizeType offset) const;
		SizeType IndexOf(const BasicString<T>& s, Case caseType, SizeType offset) const;

		SizeType LastIndexOf(ValueType s) const;
		SizeType LastIndexOf(ConstPointer s) const;
		SizeType LastIndexOf(const BasicString<T>& s) const;
		SizeType LastIndexOf(ValueType s, Case caseType) const;
		SizeType LastIndexOf(ConstPointer s, Case caseType) const;
		SizeType LastIndexOf(const BasicString<T>& s, Case caseType) const;
		SizeType LastIndexOf(ValueType s, SizeType offset) const;
		SizeType LastIndexOf(ConstPointer s, SizeType offset) const;
		SizeType LastIndexOf(const BasicString<T>& s, SizeType offset) const;
		SizeType LastIndexOf(ValueType s, Case caseType, SizeType offset) const;
		SizeType LastIndexOf(ConstPointer s, Case caseType, SizeType offset) const;
		SizeType LastIndexOf(const BasicString<T>& s, Case caseType, SizeType offset) const;

		BasicString<T>& Erase(SizeType offset, SizeType count);
		BasicString<T>& EraseFirst(SizeType count);
		BasicString<T>& EraseLast(SizeType count);

		BasicString<T>& Replace(const BasicString<T>& search, const BasicString<T>& replace);
		BasicString<T>& Replace(const BasicString<T>& search, const BasicString<T>& replace, Case caseType);

		BasicString<T>& TrimBegin();
		BasicString<T>& TrimEnd();
		BasicString<T>& Trim();
		BasicString<T>& TrimBegin(const BasicString<T>& whitespaces);
		BasicString<T>& TrimEnd(const BasicString<T>& whitespaces);
		BasicString<T>& Trim(const BasicString<T>& whitespaces);

		BasicString<T> Left(SizeType count);
		BasicString<T> Right(SizeType count);

		bool StartsWith(const BasicString<T>& str);
		bool EndsWith(const BasicString<T>& str);
		bool StartsWith(const BasicString<T>& str, Case caseType);
		bool EndsWith(const BasicString<T>& str, Case caseType);

		BasicString<T>& ToLower();
		BasicString<T>& ToUpper();

		// FIXME: ANSI/Unicode
		bool IsNumeric() const;
		bool IsHex() const;
		bool IsEmpty() const;

		// this hack is a need for GCC
		template <typename TConvert> TConvert Convert() const;
		template <typename TL> TL Convert(_BasicString_Identity<TL>) const;

		bool Convert(_BasicString_Identity<bool>) const;
		ValueType Convert(_BasicString_Identity<ValueType>) const;
		ConstPointer Convert(_BasicString_Identity<ConstPointer>) const;
		s32 Convert(_BasicString_Identity<s32>) const;
		u32 Convert(_BasicString_Identity<u32>) const;
		s64 Convert(_BasicString_Identity<s64>) const;
		u64 Convert(_BasicString_Identity<u64>) const;
		f32 Convert(_BasicString_Identity<f32>) const;
		f64 Convert(_BasicString_Identity<f64>) const;
		std::basic_string<T> Convert(_BasicString_Identity<std::basic_string<T>>) const;
		std::basic_string<T>& Convert(_BasicString_Identity<std::basic_string<T>&>) const;
		const std::basic_string<T>& Convert(_BasicString_Identity<const std::basic_string<T>&>) const;

		static BasicString<T> From(s32 s);
		static BasicString<T> From(u32 s);
		static BasicString<T> From(s64 s);
		static BasicString<T> From(u64 s);
		static BasicString<T> From(f32 s);
		static BasicString<T> From(f64 s);

		BasicString<wchar_t> ToWideChar() const;
		BasicString<char> ToMultiByte() const;

		ConstPointer GetCharArray() const;
		Pointer GetTempBuffer(SizeType n);
		BasicString<T>& ApplyTempBuffer();

		BasicString<T>& AddLine(const BasicString<T>& s);
		bool Contains(const BasicString<T>& s) const;
		bool Contains(const BasicString<T>& s, Case caseType) const;
		BasicString<T>& Insert(SizeType offset, const BasicString<T>& s);
		void Clear();

		void Swap(BasicString<T>& s);

		SizeType CountOf(ValueType s) const;
		SizeType CountOf(const BasicString<T>& s) const;
		SizeType CountOf(ValueType s, Case caseType) const;
		SizeType CountOf(const BasicString<T>& s, Case caseType) const;

		BasicString<T> Substring(SizeType offset) const;
		BasicString<T> Substring(SizeType offset, SizeType count) const;

		BasicString<T>& Reverse();
		BasicString<T>& Reserve(SizeType count);

		SizeType GetLength() const;
		SizeType LinesCount() const;

		Collection Split(ValueType separator, SplitOptions options, SizeType count) const;
		Collection Split(ValueType separator, SplitOptions options) const;
		Collection Split(ValueType separator) const;
		Collection Split(const BasicString<T>& separator, SplitOptions options, SizeType count) const;
		Collection Split(const BasicString<T>& separator, SplitOptions options) const;
		Collection Split(const BasicString<T>& separator) const;
		Collection Split(const Collections::IList<BasicString<T>>& separators, SplitOptions options, SizeType count) const;
		Collection Split(const Collections::IList<BasicString<T>>& separators, SplitOptions options) const;
		Collection Split(const Collections::IList<BasicString<T>>& separators) const;
		Collection Split(const Collections::InitializerList<BasicString<T>>& separators, SplitOptions options, SizeType count) const;
		Collection Split(const Collections::InitializerList<BasicString<T>>& separators, SplitOptions options) const;
		Collection Split(const Collections::InitializerList<BasicString<T>>& separators) const;

		//////////////////////////////////////////////////////////////////////////

		Collection Split(std::initializer_list<BasicString<T>> separators, SplitOptions options, SizeType count) const;
		Collection Split(std::initializer_list<BasicString<T>> separators, SplitOptions options) const;
		Collection Split(std::initializer_list<BasicString<T>> separators) const;

		//////////////////////////////////////////////////////////////////////////

		static BasicString<T> Format(const T* format, ...);
		static s32 CFormat(T* dest, u32 bufferSize, const T* format, ...);

		static BasicString<T> FormatV(const T* format, va_list list);
		static s32 CFormatV(T* dest, u32 bufferSize, const T* format, va_list list);

		// IList
		ConstReference At(SizeType index) const override;
		Reference At(SizeType index);

		ConstReference operator [] (SizeType index) const override;
		Reference operator [] (SizeType index) override;

		SizeType GetCount() const override;

		// IEquatable
		bool Equals(const BasicString<T>& s) const override;

		bool operator == (const BasicString<T>& r) const override;
		bool operator != (const BasicString<T>& r) const override;

		// IEnumerable
 		RZ_IENUMERABLE_IMPLEMENT_DEFAULT(_string);

	private:
		std::basic_string<T> _string;
		static ConstPointer _trueStr;

		bool Internal_EqualsNoticeCase(const BasicString<T>& s) const;
		bool Internal_EqualsIgnoreCase(const BasicString<T>& s) const;
		SizeType Internal_IndexOfNoticeCase(ValueType s, SizeType offset = 0) const;
		SizeType Internal_IndexOfNoticeCase(ConstPointer s, SizeType offset = 0) const;
		SizeType Internal_IndexOfNoticeCase(const BasicString<T>& s, SizeType offset = 0) const;
		SizeType Internal_IndexOfIgnoreCase(ValueType s, SizeType offset = 0) const;
		SizeType Internal_IndexOfIgnoreCase(ConstPointer s, SizeType offset = 0) const;
		SizeType Internal_IndexOfIgnoreCase(const BasicString<T>& s, SizeType offset = 0) const;
		SizeType Internal_LastIndexOfNoticeCase(T s, SizeType offset = 0) const;
		SizeType Internal_LastIndexOfNoticeCase(ConstPointer s, SizeType offset = 0) const;
		SizeType Internal_LastIndexOfNoticeCase(const BasicString<T>& s, SizeType offset = 0) const;
		SizeType Internal_LastIndexOfIgnoreCase(ValueType s, SizeType offset = 0) const;
		SizeType Internal_LastIndexOfIgnoreCase(ConstPointer s, SizeType offset = 0) const;
		SizeType Internal_LastIndexOfIgnoreCase(const BasicString<T>& s, SizeType offset = 0) const;
		SizeType Internal_ReplaceNoticeCase(const BasicString<T>& search, const BasicString<T>& replace);
		SizeType Internal_ReplaceIgnoreCase(const BasicString<T> &search, const BasicString<T> &replace);
		SizeType Internal_CountOfNoticeCase(const BasicString &needle) const;
		SizeType Internal_CountOfIgnoreCase(const BasicString &needle) const;

		BasicString<T>& Internal_TrimBegin(const BasicString<T>& whitespaces);
		BasicString<T>& Internal_TrimEnd(const BasicString<T>& whitespaces);

		BasicString<T>& Internal_Trim(const BasicString<T>& whitespaces);
		SizeType Internal_StrLen(const T* str) const;
};

typedef BasicString<char>		String;
typedef BasicString<wchar_t>	WString;
typedef BasicString<TCHAR>		TString;

}

#include "String.inl"

